#pragma once

#include "index.hpp"
#include "util.hpp"
#include <unordered_map>
#include <algorithm>
#include <jsoncpp/json/json.h>

#include "Log.hpp"

namespace ns_searcher{

    struct InvertedElemPrint{
        uint64_t doc_id;
        int weight;
        std::vector<std::string> words;
        InvertedElemPrint() : doc_id(0), weight(0) {}
    };

    class Searcher{
    private:
        ns_index::index *_index;  //提供系统进行查找的索引
    public:
        Searcher() {}
        ~Searcher() {}

    public:
        //初始化Searcher  就是要先根据input文件建立索引
        void InitSearcher(const std::string &input){
    
            // 根据input获取一个索引index  index需要单例模式化
            _index = ns_index::index::Getinstance();
            LOG(INFO,"获取index单例成功");

            // 根据index 建立索引
            _index->BulidIndex(input);
            LOG(INFO,"建立索引成功");
        }

        //提供搜索的函数  根据一个quary  进行搜索，返回一个json串
        void Search(const std::string &quary, std::string *json_string){
            
            //[分词] 将quary进行分词
            std::vector<std::string> quary_words;
            ns_util::JiebaUtil::JiebaStringCut(quary, &quary_words);
            //[触发] 根据quary的分词结果，进行index索引
            
            // 存放一个关键词所对应的elem
            std::vector<InvertedElemPrint> invertedlist_all; 
            
            std::unordered_map<uint64_t, InvertedElemPrint> tokens_map;  //利用其进行去重
            
            //根据搜索关键词获取 倒排拉链
            for (std::string s : quary_words){
                boost::to_lower(s);
                ns_index::inverted_list *invertedlist = _index->GetInvertedList(s);
                if (nullptr == invertedlist)    continue;

                for (const auto &elem : *invertedlist){
                    auto &elemPrint = tokens_map[elem.doc_id];
                    elemPrint.doc_id = elem.doc_id;
                    elemPrint.weight += elem.weight;
                    elemPrint.words.push_back(elem.word);
                }
            }
            
            for (const auto &elem : tokens_map){
                invertedlist_all.push_back(std::move(elem.second));
            }
           
            //[合并排序] 根据index搜索结果进行合并并且排序（根据weight）

            // std::sort(invertedlist_all.begin(), invertedlist_all.end(),
            //           [](const ns_index::nvertedElem &e1, const ns_index::nvertedElem &e2)
            //           {
            //               return e1.weight > e2.weught;
            //           });
            std::sort(invertedlist_all.begin(), invertedlist_all.end(),\
                      [](const InvertedElemPrint &e1, const InvertedElemPrint &e2)
                      {
                          return e1.weight > e2.weight;
                      });

            //[构建]将合并的并排序的内容构建一个一个的json_string返回给浏览器，用户
            Json::Value root;
            for (auto &elem : invertedlist_all){
                ns_index::Docinfo_t *doc = _index->GetForwardIndex(elem.doc_id);
                if (nullptr == doc){
                    continue;
                }

                Json::Value e;
                e["title"] = doc->title;
                e["desc"] = GetDesc(doc->content, elem.words[0]); // 这里要的是文档的部分信息，并非全部信息，即摘要
                e["url"] = doc->url;

                //for debug
                // e["id"] = (int)elem.doc_id;
                // e["weight"] = elem.weight; //int->string

                root.append(e);
            }

            //Json::StyledWriter writer;
            Json::FastWriter writer;
            *json_string = writer.write(root);
        }
        // 截取关键词附近，前50个字节，后100的字节的内容当摘要
        std::string GetDesc(const std::string &html_content, const std::string &word)
        {
            const int prev_len = 50;
            const int next_len = 100;
            //html_content的数据是原始的，但是这里的word是小写的，就有可能再查找的时候查找不到
            // int pos = html_content.find(word);
            // if (pos == std::string::npos){
            //     return "None";
            // }
            auto iter = std::search(html_content.begin(),html_content.end(),word.begin(),word.end(),\
            [](int x,int y){   return std::tolower(x) == std::tolower(y); } );
            if(iter==html_content.end())     return "None";
            
            int pos = std::distance(html_content.begin(),iter);
            
            int start = 0;
            int end = html_content.size() - 1;

            if (pos - prev_len > start)  start = pos - prev_len;
            
            if (pos + next_len < end)    end = pos + next_len;

            if (start >= end)    return "None";
            
            std::string desc = html_content.substr(start, end - start);
            desc += "...";

            return desc;
        }
    };
}